/*********************************************************************************
Organization 					: 				Georgia Institute of Technology
  
Institute 						:				Cognitive Computing Group(CCL)
 
Authors							: 				Santiago Ontanon, Kinshuk Mishra
 												Neha Sugandh 
 												
Class							:				SubGoalPlan
 
Function						: 				Provides a Plan for accomplishing a 
												goal
****************************************************************************/
package planninglanguage.plans;

import java.util.LinkedList;

import mre.MRE;

import org.jdom.Element;

import planninglanguage.conditions.Condition;
import planninglanguage.goals.Goal;
import planninglanguage.softconditions.SoftCondition;
import util.XMLWriter;
import base.WargusGameState;
import cbr.CBR;

// are SubGoalPlans ever children or are they always the root node???
// if always root then some optimizations could be done.. especially in MRE
public class SubGoalPlan extends Plan implements Cloneable {
	
	protected Goal m_subGoal;
	protected Plan m_plan = null; // could be null.. have to handle in saving
	protected int m_planAttempts;
	protected SoftCondition m_successSoftCondition;

	public SubGoalPlan(Goal a_subGoal,Condition a_precondition,Condition a_aliveCondition,Condition a_successCondition)
	{
		super(a_precondition,a_aliveCondition,a_successCondition);
		m_subGoal = a_subGoal;
		m_planAttempts = 0;
		m_successSoftCondition = null;
	}

	public SubGoalPlan(Goal a_subGoal,Condition a_precondition,Condition a_aliveCondition,Condition a_successCondition, Plan a_parent)
	{
		super(a_precondition,a_aliveCondition,a_successCondition);
		m_subGoal = a_subGoal;
		m_planAttempts = 0;
		m_parent = a_parent;
		m_successSoftCondition = null;
	}
	
	public void getOpenGoals(LinkedList<SubGoalPlan> openGoals) {
		if (getPlan() == null || getStatus() == Plan.PENDING ||
				getStatus() == Plan.FAILED) {
			openGoals.add(this);
		} else {
			getPlan().getOpenGoals(openGoals);
		}
	}
	
	public void updateParentHierarchy(Plan parent) {
		setParent(parent);
		if (getPlan() != null)
			getPlan().updateParentHierarchy(this);
	}
	
	public void resetPlanStatus(WargusGameState a_currentGameState) {
		resetConditions();
		setStatus(Plan.PENDING);
		resetConditions(a_currentGameState);			
		setAttempts(0);
	}
	
	public void getExecutableActions(LinkedList<BasicOperatorPlan> executableActions) {
		if (getPlan() != null)
			getPlan().getExecutableActions(executableActions);
	}
	
	public void getTentativeActions(LinkedList<BasicOperatorPlan> tentativeActions) {
		if (getPlan() != null)
			getPlan().getTentativeActions(tentativeActions);
	}
	
	public void getExecutingIds(LinkedList<Integer> executingUnits) {
		if (getPlan() != null)
			getPlan().getExecutingIds(executingUnits);
	}
	
	/*
					if(sp.getStatus()==Plan.SUCCEEDED) {
						if(l_plan.getSuccessCondition()==null ||
						   l_plan.getSuccessCondition().test(m_currentGameState)) {
							l_plan.setStatus(Plan.SUCCEEDED);
									
//							m_trace.createSuccessEntry(m_currentGameState, (Plan)l_plan,m_pb.getCurrCycle());			
							m_cbr.retain(sp, ((SubGoalPlan)l_plan).getSuccessSoftCondition().test(m_currentGameState),
										 m_pb.getCurrCycle()-sp.getExecuteStartCycle());
						} else {
							if (l_plan.getExecuteFinishCycle()==-1) {
								l_plan.setExecuteFinishCycle(m_pb.getCurrCycle());
							} else {
								long cycleDifference = m_pb.getCurrCycle()-l_plan.getExecuteFinishCycle();
								if (cycleDifference>BEHAVIOR_SUCCESS_LIMIT) {
									DEBUG(2, "MRE: Behavior Finished, but the goal is still not achieved. Timed out");
									l_plan.setStatus(Plan.PENDING);	
									m_cbr.retain(sp, ((SubGoalPlan)l_plan).getSuccessSoftCondition().test(m_currentGameState),
												 m_pb.getCurrCycle()-sp.getExecuteStartCycle());
									
//									m_trace.createFailureEntry(m_currentGameState, (Plan)l_plan,m_pb.getCurrCycle(),l_plan.getFailedConditions(m_currentGameState), Trace.FailureConditionType.POSTCONDITION);									
								} else {
									DEBUG(2, "MRE: Behavior Finished, but the goal is still not achieved. Waiting... " + cycleDifference + "/" + BEHAVIOR_SUCCESS_LIMIT);
								}
							}
							
						
						}							
					}
					if(sp.getStatus()==Plan.EXECUTING)
					{
						l_plan.setStatus(Plan.EXECUTING);
						sp.setExecuteStartCycle(m_pb.getCurrCycle());
					}
				}
	 */
	
	public void updateStatus(WargusGameState a_currentGameState, long a_curCycle, CBR a_cbr) {
		if (statusComplete() || m_plan == null) return;
		
		m_plan.updateStatus(a_currentGameState, a_curCycle, a_cbr);
		if (m_plan.getStatus() == Plan.FAILED) {		
			// Just in case, check to see if the goal has been achieved:
			if (getSuccessCondition() == null ||
					getSuccessCondition().test(a_currentGameState)) {
				setStatus(Plan.SUCCEEDED);

//					m_trace.createSuccessEntry(m_currentGameState, (Plan)l_plan,m_pb.getCurrCycle());			
				a_cbr.retain(m_plan, getSuccessSoftCondition().test(a_currentGameState),
							 a_curCycle - m_plan.getExecuteStartCycle());
			} else {						
//					m_trace.createFailureEntry(m_currentGameState ,(Plan)l_plan,m_pb.getCurrCycle(),getFailedConditions(m_currentGameState), Trace.FailureConditionType.DONTCARE);						

				setStatus(Plan.PENDING);
				a_cbr.retain(m_plan, getSuccessSoftCondition().test(a_currentGameState),
							 a_curCycle - m_plan.getExecuteStartCycle());
			}
		} else if (m_plan.getStatus() == Plan.SUCCEEDED) {
			if (getSuccessCondition() == null ||
					getSuccessCondition().test(a_currentGameState)) {
				setStatus(Plan.SUCCEEDED);
						
//					m_trace.createSuccessEntry(m_currentGameState, (Plan)l_plan,m_pb.getCurrCycle());			
				a_cbr.retain(m_plan, getSuccessSoftCondition().test(a_currentGameState),
							 a_curCycle - m_plan.getExecuteStartCycle());
			} else {
				if (getExecuteFinishCycle()==-1) {
					setExecuteFinishCycle(a_curCycle);
				} else {
					long cycleDifference = a_curCycle - getExecuteFinishCycle();
					if (cycleDifference > MRE.BEHAVIOR_SUCCESS_LIMIT) {
						MRE.DEBUG(2, "MRE: Behavior Finished, but the goal is still not achieved. Timed out");
						setStatus(Plan.PENDING);	
						a_cbr.retain(m_plan, getSuccessSoftCondition().test(a_currentGameState),
									 a_curCycle-m_plan.getExecuteStartCycle());
						
//							m_trace.createFailureEntry(m_currentGameState, (Plan)l_plan,m_pb.getCurrCycle(),getFailedConditions(m_currentGameState), Trace.FailureConditionType.POSTCONDITION);									
					} else {
						MRE.DEBUG(2, "MRE: Behavior Finished, but the goal is still not achieved. Waiting... "
								+ cycleDifference + "/" + MRE.BEHAVIOR_SUCCESS_LIMIT);
					}
				}
				
			
			}							
		} else if (m_plan.getStatus() == Plan.EXECUTING) {
			setStatus(Plan.EXECUTING);
			m_plan.setExecuteStartCycle(a_curCycle);
		}
	}
	
	public void substitutePlan(Plan find, Plan replace) {
		if (m_plan != null) {
			if (m_plan == find) {
				m_plan = replace;
			} else {
				m_plan.substitutePlan(find, replace);
			}
		}
	}
	
	public Object clone()
	{
		Goal cloneGoal = (Goal)m_subGoal.clone();
		SubGoalPlan clone = new SubGoalPlan(cloneGoal,null,null,null, m_parent);
		if (m_plan==null) {
			clone.setPlan(null);
		} else {
			Plan clonePlan = (Plan)m_plan.clone();
			clonePlan.setParent(clone);
			clone.setPlan(clonePlan);
			clone.setAttempts(m_planAttempts);		
		}
		return clone;
	}
	
	public void resetConditions(WargusGameState a_gamestate) 
	{
		if (a_gamestate != null) {
			m_successCondition = m_subGoal.generateSuccessCondition(a_gamestate);
			if (m_successCondition != null) m_successCondition.reset();
			m_successSoftCondition = m_subGoal.generateSuccessSoftCondition(a_gamestate);
			if (m_successSoftCondition != null) m_successSoftCondition.reset();			
		} // if		
	}
	
	public Plan getPlan()
	{
		return m_plan;
	}

	public void setPlan(Plan a_plan)
	{
		m_plan = a_plan;
	}
	
	public Condition getCondition() 
	{
		return m_successCondition;
	}

	public SoftCondition getSuccessSoftCondition() 
	{
		return m_successSoftCondition;
	}

	public int getAttempts()
	{
		return m_planAttempts;
	}

	public void setAttempts(int l_numAttempts)
	{
		m_planAttempts=l_numAttempts;
	}
	public void incAttempt()
	{
		m_planAttempts++;
	}
	public Goal getGoal()
	{
		return m_subGoal;
	}

	public Condition getStateSuccessCondition(WargusGameState a_gamestate)
	{
		return m_subGoal.generateSuccessCondition(a_gamestate);
	}

	public String toString(int tabs) {
		String str= "";
		int i;

		for(i=0;i<tabs;i++) str = str + "  ";
		str = str + "SubGoalPlan(status = " + STATUS_NAME[m_status] + " , goal = " + m_subGoal.toString();

		if (m_status!=SUCCEEDED) {		
			if (m_plan != null) {
				str = str + "\n" + m_plan.toString(tabs+1) + "\n";
				for(i=0;i<tabs;i++) str = str + "  ";
			} else {
//				for(i=0;i<tabs+1;i++) str = str + "  ";
//				str = str + "null\n";
//				for(i=0;i<tabs;i++) str = str + "  ";
			}
		}
		str = str + ")";
		str = str + /* "\n" + */ conditionsToString(tabs);
		return str;
	} 

	public void writeXML(XMLWriter w) {
		w.tag("subgoal-plan" + super.XMLplanAttributes());
		{
			m_subGoal.writeXML(w);
			w.tag("plan");
			{
				Plan.writeXML(w, m_plan);
			}
			w.tag("/plan");
			w.tag("plan-attempts", m_planAttempts);
			// XXX don't need to save the soft conditions?
			super.writePlanAttrsXML(w);
		}
		w.tag("/subgoal-plan");
	}
	public static SubGoalPlan loadSGPFromXML(Element e) {
		assert e.getName().equals("subgoal-plan") :
			"SubGoalPlan.loadFromXML: Invalid XML Element " + e.getName();
		
		Goal goal = Goal.loadGoalFromXML(e.getChild("goal"));
		Plan plan = Plan.loadPlanFromXML((Element) e.getChild("plan").getChildren().get(0));
		int planAttempts = Integer.parseInt(e.getChildText("plan-attempts"));
		
		SubGoalPlan sgp = new SubGoalPlan(goal, null, null, null);
		sgp.setAttempts(planAttempts);
		sgp.setPlan(plan);
		Plan.setPlanAttrsFromXML(plan, e);
		return sgp;
	}
	
}

